package com.example.elasticsearchdemo.controller;

import cn.hutool.core.util.IdUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.example.elasticsearchdemo.model.Poetry;
import com.example.elasticsearchdemo.model.PoetryDTO;
import com.example.elasticsearchdemo.repository.PoetryRepository;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.action.admin.indices.analyze.AnalyzeAction;
import org.elasticsearch.action.admin.indices.analyze.AnalyzeRequestBuilder;
import org.elasticsearch.action.admin.indices.analyze.AnalyzeResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.update.UpdateResponse;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.Operator;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cglib.beans.BeanMap;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.query.*;
import org.springframework.web.bind.annotation.*;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static org.elasticsearch.index.query.QueryBuilders.*;


/**
 * @Author yanggld
 * @Date 2019/7/3-9:31
 * @Description
 */
@Slf4j
@RestController
@RequestMapping("/poetry")
public class PoetryController {

	@Autowired
	PoetryRepository poetryRepository;

	@Autowired
	private ElasticsearchTemplate elasticsearchTemplate;

	private List<Poetry> buildPoetry() {
		List<Poetry> result = new ArrayList<>();
		List<String> titleList = getTitle();
		List<String> contentList = getContent();
		for (int i = 0; i < titleList.size(); i++) {
			String title = titleList.get(i);
			String content = contentList.get(i);
			Poetry model = new Poetry();
			model.setId(IdUtil.getSnowflake(1, 1).nextIdStr());
			model.setTitle(title);
			model.setContent(content);
			model.setSort(i);
			result.add(model);
		}
		return result;
	}


	@PostMapping("/saveOne")
	public String saveOne() {
		String documentId = "1234567";
		Poetry model = new Poetry();
		model.setId(documentId);
		model.setTitle("some message");
		IndexQuery indexQuery = new IndexQueryBuilder().withId(model.getId()).withObject(model).build();
		elasticsearchTemplate.index(indexQuery);
		return "index completed.";
	}


	@PostMapping("/save")
	public String save() {
		List<Poetry> poetryList = buildPoetry();
		try {
			List<IndexQuery> indexQueryList = new ArrayList<>();
			for (int i = 0; i < poetryList.size(); i++) {
				Poetry model = poetryList.get(i);
				IndexQuery indexQuery = new IndexQueryBuilder().withObject(model).build();
				// 每20存储一次
				int eachCount = 20;
				indexQueryList.add(indexQuery);
				if (i % eachCount == 0) {
					elasticsearchTemplate.bulkIndex(indexQueryList);
					indexQueryList.clear();
					System.out.println("bulkIndex counter : " + i);
				}
			}
			if (indexQueryList.size() > 0) {
				elasticsearchTemplate.bulkIndex(indexQueryList);
			}
			log.info("bulkIndex completed.");
		} catch (Exception e) {
			log.info("IndexerService.bulkIndex exception:{}", e.getMessage());
			throw e;
		}
		return "bulkIndex completed.";
	}

	/**
	 * 查询所有
	 * 不加分页默认查询10条数据
	 */
	@GetMapping("/all")
	public List<Poetry> findAll() {
		SearchQuery searchQuery = new NativeSearchQueryBuilder()
				.withQuery(matchAllQuery())
				.withPageable(PageRequest.of(0, 100, Sort.Direction.ASC, "sort"))
				.build();
		List<Poetry> list = elasticsearchTemplate.queryForList(searchQuery, Poetry.class);
		return list;
	}

	@GetMapping("/{id}")
	public String getById(@PathVariable String id) {
		SearchQuery searchQuery = new NativeSearchQueryBuilder()
				.withQuery(queryStringQuery(id).field("id"))
				.build();
		List<Poetry> list = elasticsearchTemplate.queryForList(searchQuery, Poetry.class);
		return JSON.toJSONString(list);
	}

	/**
	 * 模糊匹配带有 查询关键字的
	 * 如查询 蝶恋花
	 * 结果中会包含 《醉花阴·薄雾浓云愁永昼》
	 *
	 * @param title
	 * @return
	 */
	@GetMapping("/getByTitle")
	public String getByTitle(@RequestParam String title) {
		SearchQuery searchQuery = new NativeSearchQueryBuilder()
				.withQuery(matchQuery("title", title))
				.build();
		List<Poetry> list = elasticsearchTemplate.queryForList(searchQuery, Poetry.class);
		return JSON.toJSONString(list);
	}

	/**
	 * 包含查询条件的
	 * 如查询 蝶恋花
	 * 结果中都是包含 蝶恋花。如《蝶恋花 离情》
	 *
	 * @param title
	 * @return
	 */
	@GetMapping("/containTitle")
	public String containTitle(@RequestParam String title) {
		SearchQuery searchQuery = new NativeSearchQueryBuilder()
				.withQuery(
						matchQuery("title", title)
								// 默认使用的是or
								.operator(Operator.AND)
								// 匹配率
								.minimumShouldMatch("100%")
				)
				.build();
		List<Poetry> list = elasticsearchTemplate.queryForList(searchQuery, Poetry.class);
		return JSON.toJSONString(list);
	}


	/**
	 * 指定最终查询结果填充数据的字段
	 *
	 * @return
	 */
	@GetMapping("/filedFilter")
	public Object filedFilter() {
		//只显示名字和价格
		String[] include = {"title"};
		//两个参数分别是要显示的和不显示的
		FetchSourceFilter fetchSourceFilter = new FetchSourceFilter(include, null);

		SearchQuery searchQuery = new NativeSearchQueryBuilder()
				.withQuery(queryStringQuery("如梦令"))
				.withSourceFilter(fetchSourceFilter)
				.build();
		List<Poetry> list = elasticsearchTemplate.queryForList(searchQuery, Poetry.class);
		return list;
	}

	/**
	 * bool查询
	 */
	@GetMapping("/bool")
	public List<Poetry> bool() {
		SearchQuery searchQuery = new NativeSearchQueryBuilder()
				.withFilter(
						boolQuery()
								.must(matchQuery("title", "如梦令"))
				)
				.build();
		List<Poetry> list = elasticsearchTemplate.queryForList(searchQuery, Poetry.class);
		return list;
	}

	/**
	 * filter查询
	 * sort大于10的结果
	 */
	@GetMapping("/filter")
	public List<Poetry> filter() {
		SearchQuery searchQuery = new NativeSearchQueryBuilder()
				.withFilter(
						boolQuery()
								.must(matchQuery("title", "如梦令"))
								.filter(
										rangeQuery("sort")
												.gte(10)
								)
				)
				.build();
		List<Poetry> list = elasticsearchTemplate.queryForList(searchQuery, Poetry.class);
		return list;
	}

	/**
	 * phrase查询
	 * matchPhraseQuery不会被分词器分词，而是直接以一个短语的形式查询，
	 * 而如果你在创建索引所使用的field的value中没有这么一个短语（顺序无差，且连接在一起），那么将查询不出任何结果
	 * matchQuery等，在执行查询时，搜索的词会被分词器分词
	 */
	@GetMapping("/phrase")
	public Object phrase() {
		SearchQuery searchQuery = new NativeSearchQueryBuilder()
				.withQuery(
						matchPhraseQuery("title", "如令")
				)
				.build();
		List<Poetry> list = elasticsearchTemplate.queryForList(searchQuery, Poetry.class);

		SearchQuery searchQuery1 = new NativeSearchQueryBuilder()
				.withQuery(matchQuery("title", "如令"))
				.build();
		List<Poetry> list1 = elasticsearchTemplate.queryForList(searchQuery1, Poetry.class);
		log.info("短语查询结果:" + list);
		log.info("分词查询结果:" + list1);
		return list;
	}


	/**
	 * highlight查询
	 */
	@GetMapping("/highlight")
	public Object highlight() {

		//高亮显示的词必须是查询的词
		final List<HighlightBuilder.Field> fields = new HighlightBuilder().field("content").fields();

		SearchQuery searchQuery = new NativeSearchQueryBuilder()
				.withQuery(
						matchPhraseQuery("content", "梦")
				)
				.withHighlightFields(fields.toArray(new HighlightBuilder.Field[0]))
				.build();

		//如果不想使用分页的写法
		List<PoetryDTO> result = elasticsearchTemplate.query(searchQuery, response -> {
			List<PoetryDTO> list = new ArrayList<>();
			for (SearchHit searchHit : response.getHits()) {
				//没有数据
				if (response.getHits().getHits().length <= 0) {
					return null;
				}
				log.info(searchHit.getSourceAsString());
//				//hit转换成bean
				PoetryDTO dto = mapToBean(searchHit.getSourceAsMap(), new PoetryDTO());
				dto.setHighlighted(searchHit.getHighlightFields().get("content").fragments()[0].toString());
				list.add(dto);
			}
			return list;
		});
		return result;
	}


	/**
	 * 根据id删除
	 */
	@DeleteMapping("/{id}")
	public String deleteById(@PathVariable String id) {
		String _id = elasticsearchTemplate.delete(Poetry.class, id);
		return _id;
	}


	/**
	 * 根据指定字段删除
	 * 不是完全匹配,类似模糊匹配
	 */
	@DeleteMapping("/deleteByCond/{title}")
	public void deleteByCond(@PathVariable String title) {
		DeleteQuery deleteQuery = new DeleteQuery();
		//类似模糊删除
		deleteQuery.setQuery(termQuery("title", title));
		elasticsearchTemplate.delete(deleteQuery, Poetry.class);
	}

	/**
	 * 更新
	 */
	@PutMapping
	public String update(@RequestBody Poetry model) {
		IndexRequest indexRequest = new IndexRequest();
		Map<String, Object> map = beanToMap(model);
		//更新整个实体
		indexRequest.source(map, XContentType.JSON);
		//更新指定的字段
//        indexRequest.source("title",model.getTitle());
		UpdateQuery updateQuery = new UpdateQueryBuilder().withId(model.getId())
				.withClass(Poetry.class).withIndexRequest(indexRequest).build();
		UpdateResponse response = elasticsearchTemplate.update(updateQuery);
		return JSONObject.toJSONString(response);
	}

	/**
	 * 批量更新
	 */
	@PutMapping("/updateBatch")
	public void updateBatch(@RequestBody List<Poetry> list) {
		List<UpdateQuery> queries = new ArrayList<>();
		for (Poetry model : list) {
			IndexRequest indexRequest = new IndexRequest();
			Map<String, Object> map = beanToMap(model);
			//更新整个实体
			indexRequest.source(map, XContentType.JSON);
			UpdateQuery updateQuery = new UpdateQueryBuilder().withId(model.getId())
					.withClass(Poetry.class).withIndexRequest(indexRequest).build();
			queries.add(updateQuery);
		}
		elasticsearchTemplate.bulkUpdate(queries);

	}

	@GetMapping("/get")
	public List<Poetry> get() {
		SearchQuery searchQuery = new NativeSearchQueryBuilder()
				.withQuery(
						matchQuery("content", "香花已尽")
								.analyzer("ik_smart")   //mapping没有设置的时候这里可以设置分词类型
				)
				.build();
		List<Poetry> poetries = elasticsearchTemplate.queryForList(searchQuery, Poetry.class);
		return poetries;
	}

	/*
	 * index 索引index
	 * text 需要被分析的词语
	 * 默认使用中文ik_smart分词
	 * */
	@GetMapping("/analyze")
	public String[] getAnalyzes(@RequestParam String analyzer,@RequestParam String content){
		//调用ES客户端分词器进行分词
		AnalyzeRequestBuilder ikRequest = new AnalyzeRequestBuilder(elasticsearchTemplate.getClient(),
				AnalyzeAction.INSTANCE,"article",content).setAnalyzer(analyzer);
		List<AnalyzeResponse.AnalyzeToken> ikTokenList = ikRequest.execute().actionGet().getTokens();

		// 赋值
		List<String> searchTermList = new ArrayList<>();
		ikTokenList.forEach(ikToken -> { searchTermList.add(ikToken.getTerm()); });

		return searchTermList.toArray(new String[searchTermList.size()]);
	}


	public <T> T mapToBean(Map<String, Object> map, T bean) {
		BeanMap beanMap = BeanMap.create(bean);
		beanMap.putAll(map);
		return bean;
	}

	public <T> Map<String, Object> beanToMap(T bean) {
		Map<String, Object> map = new HashMap<>();
		if (bean != null) {
			BeanMap beanMap = BeanMap.create(bean);
			for (Object key : beanMap.keySet()) {
				map.put(key + "", beanMap.get(key));
			}
		}
		return map;
	}


	private List<String> getTitle() {
		List<String> list = new ArrayList<>();
		list.add("《如梦令·常记溪亭日暮》");
		list.add("《醉花阴·薄雾浓云愁永昼》");
		list.add("《声声慢·寻寻觅觅》");
		list.add("《永遇乐·落日熔金》");
		list.add("《如梦令·昨夜雨疏风骤》");
		list.add("《渔家傲·雪里已知春信至》");
		list.add("《点绛唇·蹴[1]罢秋千》");
		list.add("《点绛唇·寂寞深闺》");
		list.add("《蝶恋花·泪湿罗衣脂粉满》");
		list.add("《蝶恋花 离情》");
		list.add("《浣溪沙》");
		list.add("《浣溪沙》");
		list.add("《浣溪沙》");
		list.add("《浣溪沙》");
		list.add("《浣溪沙》");
		list.add("《减字木兰花·卖花担上》");
		list.add("《临江仙·欧阳公作《蝶恋花》");
		list.add("《临江仙·庭院深深深几许》");
		list.add("《念奴娇·萧条庭院》");
		list.add("《菩萨蛮·风柔日薄春犹早》");
		list.add("《菩萨蛮·归鸿声断残云碧》");
		list.add("《武陵春·风住尘香花已尽》");
		list.add("《一剪梅·红藕香残玉蕈秋》");
		list.add("《渔家傲·天接云涛连晓雾》");
		list.add("《鹧鸪天·暗淡轻黄体性柔》");
		list.add("《鹧鸪天·寒日萧萧上锁窗》");
		list.add("《一剪梅·红藕香残玉簟秋》");
		list.add("《如梦令·常记溪亭日暮》");
		list.add("《浣溪沙》");
		list.add("《浣溪沙》");
		list.add("《浣溪沙》");
		list.add("《蝶恋花·泪湿罗衣脂粉满》");
		list.add("《蝶恋花·暖日晴风初破冻》");
		list.add("《鹧鸪天·寒日萧萧上锁窗》");
		list.add("《醉花阴·薄雾浓云愁永昼》");
		list.add("《鹧鸪天·暗淡轻黄体性柔》");
		list.add("《蝶恋花·永夜恹恹欢意少》");
		list.add("《浣溪沙》");
		list.add("《浣溪沙》");
		list.add("《如梦令·谁伴明窗独坐》");
		return list;
	}

	private List<String> getContent() {
		List<String> list = new ArrayList<>();
		list.add("初中 宋·李清照 常记溪亭日暮，沉醉不知归路，兴尽晚回舟，误入藕花深处。争渡，争渡");
		list.add("重阳节 宋·李清照 薄雾浓云愁永昼，瑞脑消金兽。佳节又重阳，玉枕纱厨，半夜凉初透。东");
		list.add("闺怨诗 宋·李清照 寻寻觅觅，冷冷清清，凄凄惨惨戚戚。乍暖还寒时候，最难将息。三杯两");
		list.add("元宵节 宋·李清照 落日熔金，暮云合璧，人在何处。染柳烟浓，吹梅笛怨，春意知几许。元");
		list.add("婉约诗 宋·李清照 昨夜雨疏风骤，浓睡不消残酒，试问卷帘人，却道海棠依旧。知否，知否");
		list.add("描写梅花 宋·李清照 雪里已知春信至，寒梅点缀琼枝腻，香脸半开娇旖旎，当庭际，玉人浴出");
		list.add(" 宋·李清照 蹴罢秋千，起来慵整纤纤手。露浓花瘦，薄汗轻衣透。见客入来，袜刬金");
		list.add("闺怨诗 宋·李清照 寂寞深闺，柔肠一寸愁千缕。惜春春去。几点催花雨。倚遍阑干，只是无");
		list.add("婉约诗 宋·李清照 泪湿罗衣脂粉满。四叠阳关，唱到千千遍。人道山长水又断。萧萧微雨闻");
		list.add("描写春天 宋·李清照 暖雨晴风初破冻，柳眼梅腮，已觉春心动。酒意诗情谁与共？泪融残粉花");
		list.add("寒食节 宋·李清照 淡荡春光寒食天，玉炉沈水袅残烟，梦回山枕隐花钿。海燕未来人斗草，");
		list.add(" 宋·李清照 髻子伤春慵更梳，晚风庭院落梅初，淡云来往月疏疏，玉鸭薰炉闲瑞脑，");
		list.add(" 宋·李清照 莫许杯深琥珀浓，未成沉醉意先融。疏钟已应晚来风。瑞脑香消魂梦断，");
		list.add("闺怨诗 宋·李清照 小院闲窗春已深，重帘未卷影沉沉。倚楼无语理瑶琴，远岫出山催薄暮。");
		list.add("爱情诗 宋·李清照 绣幕芙蓉一笑开，斜偎宝鸭亲香腮，眼波才动被人猜。一面风情深有韵，");
		list.add("描写春天 宋·李清照 卖花担上，买得一枝春欲放。泪染轻匀，犹带彤霞晓露痕。怕郎猜道，奴");
		list.add("》 宋·李清照 欧阳公作《蝶恋花》，有“深深深几许”之句，予酷爱之。用其语作“庭");
		list.add("描写梅花 宋·李清照 庭院深深深几许，云窗雾阁春迟，为谁憔悴损芳姿。夜来清梦好，应是发");
		list.add("寒食节 宋·李清照 萧条庭院，又斜风细雨，重门须闭。宠柳娇花寒食近，种种恼人天气。险");
		list.add("思乡诗 宋·李清照 风柔日薄春犹早，夹衫乍著心情好。睡起觉微寒，梅花鬓上残。故乡何处");
		list.add("描写春天 宋·李清照 归鸿声断残云碧，背窗雪落炉烟直。烛底凤钗明，钗头人胜轻。角声催晓");
		list.add("闺怨诗 宋·李清照 风住尘香花已尽，日晚倦梳头。物是人非事事休，欲语泪先流。闻说双溪");
		list.add(" 宋·李清照 红藕香残玉蕈秋，轻解罗裳，独上兰舟。云中谁寄锦书来？雁字回时，月");
		list.add("豪放诗 宋·李清照 天接云涛连晓雾。星河欲转千帆舞。仿佛梦魂归帝所。闻天语。殷勤问我");
		list.add("描写花 宋·李清照 暗淡轻黄体性柔。情疏迹远只香留。何须浅碧深红色，自是花中第一流。");
		list.add("描写秋天 宋·李清照 寒日萧萧上琐窗，梧桐应恨夜来霜。酒阑更喜团茶苦，梦断偏宜瑞脑香。");
		list.add("闺怨诗 宋·李清照 红藕香残玉簟秋。轻解罗裳，独上兰舟。云中谁寄锦书来？雁字回时，月");
		list.add(" 宋·李清照 常记溪亭日暮。沈醉不知归路。兴尽晚回舟，误入藕花深处。争渡。争渡");
		list.add(" 宋·李清照 莫许杯深琥珀浓。未成沈醉意先融。已应晚来风。瑞脑香消魂梦断，");
		list.add(" 宋·李清照 小院闲窗春色深。重帘未卷影沈沈。倚楼无语理瑶琴。远岫出山催薄暮，");
		list.add(" 宋·李清照 淡荡春光寒食天。玉炉沈水袅残烟。梦回山枕隐花钿。海燕未来人斗草，");
		list.add(" 宋·李清照 泪湿罗衣脂粉满。四叠阳关，唱到千千遍。人道山长山又断。萧萧微雨闻");
		list.add(" 宋·李清照 暖日晴风初破冻。柳眼眉腮，已觉春心动。酒意诗情谁与共。泪融残粉花");
		list.add(" 宋·李清照 寒日萧萧上锁窗。梧桐应恨夜来霜。酒阑更喜团茶苦，梦断偏宜瑞脑香。");
		list.add(" 宋·李清照 薄雾浓云愁永昼。瑞脑消金兽。佳节又重阳，玉枕纱厨，半夜凉初透。东");
		list.add(" 宋·李清照 暗淡轻黄体性柔。情疏迹远只香留。何须浅碧深红色，自是花中第一流。");
		list.add(" 宋·李清照 永夜恹恹欢意少。空梦长安，认取长安道。为报今年春色好。花光月影宜");
		list.add(" 宋·李清照 髻子伤春慵更梳。晚风庭院落梅初。淡云来往月疏疏。玉鸭熏炉闲瑞脑，");
		list.add(" 宋·李清照 绣面芙蓉一笑开。斜飞宝鸭衬香腮。眼波才动被人猜。一面风情深有韵，");
		list.add(" 宋·李清照 谁伴明窗独坐，我共影儿俩个。灯尽欲眠时，影也把人抛躲。无那，无那");
		return list;
	}
}
